// opencv async detector demo
// Author: lihaoweicv@hotmail.com
// MIT License

#include "runner/async_runner.h"

namespace cvf
{

    Async_Preview_Runner::Async_Preview_Runner()
    {
        _frameReady = false;
        _detector = nullptr;
        _maxFPS = 5;
        _doSubJob = false;
        _isRunning = false;
        _modelInited = false;
        _subJob = nullptr;
        _event = NORMAL_RUNNING;
    }
    Async_Preview_Runner::~Async_Preview_Runner()
    {
        //not responsible resource
    }
    void Async_Preview_Runner::bind_detector(std::shared_ptr<Abstract_Detector> &detector)
    {
        _detector = detector;
    }


    void Async_Preview_Runner::init_models_thread_fn()
    {
        Status s = _detector->init_models(_modelPaths);
        if(s != Status_OK)
        {
            LOGE("model init failed!\n");
            return;
        }
        _modelInited = true;
    }
    Status Async_Preview_Runner::init_models(std::vector<std::string> &modelPaths,bool  isAsync)
    {
        if(_detector == nullptr)
        {
            LOGE("_detector == nullptr, please first bind detector!\n");
            return Status_PARAM_ERR;
        }
        _modelPaths = modelPaths;
        if(isAsync)
        {
            std::thread t;
            t = std::thread(&Async_Preview_Runner::init_models_thread_fn, this);
            t.detach();
        }
        else
        {
            Status s = _detector->init_models(_modelPaths);
            if(s != Status_OK)
            {
                LOGE("model init failed!\n");
                return Status_INIT_MODEL_ERR;
            }
            _modelInited = true;
        }

        return Status_OK;
    }

    Status Async_Preview_Runner::fetch_dets(DetInfo &dets)
    {
        dets = get_dets();
        return Status_OK;
    }

    Status Async_Preview_Runner::push_frame(cv::Mat &frameBGR)
    {
        if(!frameBGR.data)
        {
            LOGE("[Async_Preview_Runner] frame data in null!\n");
            return  Status_FRAME_DATA_INVALID;
        }
        //如果帧是ready的， 说明还没有被消费完，直接跳过
        if(frame_ready() )
        {
            return  Status_OK;
        }
        //设置完后， frame_ready() 会变成true
        set_frame(frameBGR);

        return Status_OK;
    }

    Status Async_Preview_Runner::start()
    {
        std::thread t;
        t = std::thread(&Async_Preview_Runner::thread_loop, this);
        t.detach();
        return  Status_OK;
    }

    Status Async_Preview_Runner::stop()
    {
        set_event(STOP_LOOP);
        while (is_running())
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(20));
        }
        return  Status_OK;
    }

    Status Async_Preview_Runner::submit_once_sub_detect_job(Runner_Sub_Detect_Job * job)
    {
        if(!job->imgBGR.data)
        {
            LOGE("[Async_Preview_Runner] imgBGR data in null!\n");
            return  Status_FRAME_DATA_INVALID;
        }
        if(do_sub_job() )
        {
            LOGE("[Async_Preview_Runner] previus sub job not finished!\n");
            return  Status_SUBMIT_JOB_BUSY;
        }
        _subJob = job;
        set_do_sub_job(true);
        return Status_OK;
    }

    void Async_Preview_Runner::do_sub_detect_job_fn()
    {
        LOGE("[Async_Preview_Runner] do once sub job!\n");
        //TODO  根据预览检测的历史结果，对拍摄的图检测结果进行一定过滤限制
        DetInfo subDets;
        _detector->detect_on_frame(_subJob->imgBGR, subDets);
        _subJob->callback(subDets);
        set_do_sub_job(false);
    }

    void Async_Preview_Runner::thread_loop()
    {
        set_is_running(true);

        int waitN = 0;
        while (!_modelInited)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            waitN+=1;
            if(waitN > 50)
            {
                LOGE("too long time model not inited! exit thread loop!\n");
                set_is_running(false);
                return;
            }
        }

        while(true)
        {
            RunnerEvent event = get_event();

            switch (event)
            {
                case NORMAL_RUNNING:
                {
                    cv::Mat frameBGR;
                    //如果帧还没有准备好，那么轮询等待
                    if(!frame_ready())
                    {
                        std::this_thread::sleep_for(std::chrono::milliseconds(50));
                        //等待帧期间也有可能会进行子检测任务
                        if(do_sub_job() && _subJob!= nullptr)
                        {
                            do_sub_detect_job_fn();
                        }
                        continue;
                    }
                    get_frame(frameBGR, false);

                    DetInfo dets;

                    double start = cv::getTickCount();

                    //std::cout<<"frame size: "<<frameBGR.cols<<" "<<frameBGR.rows<<std::endl;

                    _detector->detect_on_frame(frameBGR, dets);
                    set_dets(dets);
                    //保护到检测器运行完，因为没进行copy使用
                    set_frame_ready(false);

                    //可能会做一次子检测任务
                    if(do_sub_job() && _subJob!= nullptr)
                    {
                        do_sub_detect_job_fn();
                    }

                    double duration = ( (double)cv::getTickCount() - start) / cv::getTickFrequency();
                    int ms = duration * 1000;
                    int maxMS = 1000 / _maxFPS;

                    LOGI("==> [Async_Preview_Runner] fps:%f\n", 1000.0f/ms);

                    if(ms < maxMS)
                    {
                        std::this_thread::sleep_for(std::chrono::milliseconds(maxMS - ms));
                    }
                }
                break;

                case STOP_LOOP:
                {
                    LOGI("Async_Preview_Runner::thread_loop stopped!\n");
                    _event = NORMAL_RUNNING;
                    set_is_running(false);
                    return;
                }
                default:
                    break;
            }
        }
    }

}//namespace cvf

